/* -*- mode: c; c-basic-offset: 2 -*- */
%name grn_expr_parser
%token_prefix GRN_EXPR_TOKEN_
%include {
#ifdef assert
#  undef assert
#endif
#define assert GRN_ASSERT

  static void
  array_literal_end(efs_info *efsi, int n_elements)
  {
    grn_ctx *ctx = efsi->ctx;
    grn_expr *e = (grn_expr *)(efsi->e);

    grn_id uvector_domain = GRN_ID_NIL;
    int i;
    for (i = n_elements; i > 0; i--) {
      grn_expr_code *code = &(e->codes[e->codes_curr - i]);
      if (code->op != GRN_OP_PUSH) {
        ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
            "[expr][array-literal] "
            "complex expression in array literal isn't supported yet: <%.*s>",
            (int)(efsi->str_end - efsi->str),
            efsi->str);
        return;
      }
      if (!(grn_obj_is_number_family_bulk(ctx, code->value) ||
            grn_obj_maybe_record_bulk(ctx, code->value))) {
        uvector_domain = GRN_ID_NIL;
        break;
      }
      if (i == n_elements) {
        uvector_domain = code->value->header.domain;
      } else {
        if (uvector_domain != code->value->header.domain) {
          uvector_domain = GRN_ID_NIL;
          break;
        }
      }
    }

    grn_obj *array_literal;
    if (uvector_domain == GRN_ID_NIL) {
      array_literal = grn_obj_open(ctx, GRN_VECTOR, 0, GRN_ID_NIL);
    } else {
      array_literal = grn_obj_open(ctx, GRN_UVECTOR, 0, uvector_domain);
    }
    if (!array_literal) {
      ERR(GRN_NO_MEMORY_AVAILABLE,
          "[expr][array-literal] couldn't create vector: <%.*s>",
          (int)(efsi->str_end - efsi->str),
          efsi->str);
      return;
    }
    grn_expr_take_obj(ctx, efsi->e, array_literal);

    for (i = n_elements; i > 0; i--) {
      grn_expr_code *code = &(e->codes[e->codes_curr - i]);
      grn_obj *element = code->value;
      if (uvector_domain == GRN_ID_NIL) {
        grn_vector_add_element_float(ctx,
                                     array_literal,
                                     GRN_TEXT_VALUE(element),
                                     GRN_TEXT_LEN(element),
                                     0,
                                     element->header.domain);
      } else {
        grn_bulk_write(ctx, array_literal, GRN_BULK_HEAD(element), GRN_BULK_VSIZE(element));
      }
      if (ctx->rc != GRN_SUCCESS) {
        char message[GRN_CTX_MSGSIZE];
        grn_strncpy(message, sizeof(message), ctx->errbuf, sizeof(ctx->errbuf));
        ERR(ctx->rc,
            "[expr][array-literal] failed to add an element: %s",
            message);
        return;
      }
    }
    for (i = 0; i < n_elements; i++) {
      grn_expr_dfi_pop(e);
      e->codes_curr -= 1;
    }

    grn_expr_append_obj(ctx, efsi->e, array_literal, GRN_OP_PUSH, 1);
  }

  static void
  object_literal_end(efs_info *efsi, int n_properties)
  {
    grn_ctx *ctx = efsi->ctx;
    grn_hash *object_literal =
      grn_hash_create(ctx,
                      NULL,
                      GRN_TABLE_MAX_KEY_SIZE,
                      sizeof(grn_obj),
                      GRN_OBJ_KEY_VAR_SIZE|GRN_OBJ_TEMPORARY|GRN_HASH_TINY);
    if (!object_literal) {
      ERR(GRN_NO_MEMORY_AVAILABLE,
          "[expr][object-literal] couldn't create hash table: <%.*s>",
          (int)(efsi->str_end - efsi->str),
          efsi->str);
      return;
    }

    grn_expr_take_obj(ctx, efsi->e, (grn_obj *)(object_literal));
    DB_OBJ(object_literal)->header.domain = GRN_DB_SHORT_TEXT;

    grn_expr *e = (grn_expr *)(efsi->e);
    int i;
    for (i = n_properties; i > 0; i--) {
      int base = e->codes_curr - (i * 3);
      grn_obj *name = e->codes[base].value;
      grn_obj *value = e->codes[base + 2].value;

      grn_obj *buf;
      int added;
      grn_id id = grn_hash_add(ctx,
                               object_literal,
                               GRN_TEXT_VALUE(name),
                               GRN_TEXT_LEN(name),
                               (void **)(&buf),
                               &added);
      if (id == GRN_ID_NIL) {
        grn_rc rc = ctx->rc;
        if (rc == GRN_SUCCESS) {
          rc = GRN_NO_MEMORY_AVAILABLE;
        }
        ERR(rc,
            "[expr][object-literal] failed to add a property: <%.*s>",
            (int)GRN_TEXT_LEN(name),
            GRN_TEXT_VALUE(name));
        return;
      }

      if (!added) {
        ERR(GRN_INVALID_ARGUMENT,
            "[expr][object-literal] duplicated property name: <%.*s>",
            (int)GRN_TEXT_LEN(name),
            GRN_TEXT_VALUE(name));
        return;
      }

      switch (value->header.type) {
      case GRN_TABLE_HASH_KEY :
      case GRN_COLUMN_FIX_SIZE :
      case GRN_COLUMN_VAR_SIZE :
      case GRN_COLUMN_INDEX :
        GRN_OBJ_INIT(buf, GRN_PTR, 0, GRN_ID_NIL);
        GRN_PTR_SET(ctx, buf, value);
        break;
      case GRN_VECTOR :
        GRN_OBJ_INIT(buf, value->header.type, 0, value->header.domain);
        grn_vector_copy(ctx, value, buf);
        break;
      default :
        GRN_OBJ_INIT(buf, value->header.type, 0, value->header.domain);
        GRN_TEXT_PUT(ctx, buf, GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
        break;
      }
    }
    for (i = 0; i < n_properties; i++) {
      grn_expr_dfi_pop(e);
      e->codes_curr -= 3;
    }
    grn_expr_append_obj(ctx, efsi->e, (grn_obj *)(object_literal),
                        GRN_OP_PUSH, 1);
  }
}

%token_type { int }

%type suppress_unused_variable_warning { void * }
%destructor suppress_unused_variable_warning {
  (void)efsi;
}

%extra_argument { efs_info *efsi }

%syntax_error {
  {
    grn_ctx *ctx = efsi->ctx;
    grn_obj message;
    GRN_TEXT_INIT(&message, 0);
    GRN_TEXT_PUT(ctx, &message, efsi->str, efsi->cur - efsi->str);
    GRN_TEXT_PUTC(ctx, &message, '|');
    if (efsi->cur < efsi->str_end) {
      GRN_TEXT_PUTC(ctx, &message, efsi->cur[0]);
      GRN_TEXT_PUTC(ctx, &message, '|');
      GRN_TEXT_PUT(ctx, &message,
                   efsi->cur + 1, efsi->str_end - (efsi->cur + 1));
    } else {
      GRN_TEXT_PUTC(ctx, &message, '|');
    }
    if (ctx->rc == GRN_SUCCESS) {
      ERR(GRN_SYNTAX_ERROR, "Syntax error: <%.*s>",
          (int)GRN_TEXT_LEN(&message), GRN_TEXT_VALUE(&message));
    } else {
      char errbuf[GRN_CTX_MSGSIZE];
      grn_strcpy(errbuf, GRN_CTX_MSGSIZE, ctx->errbuf);
      ERR(ctx->rc, "Syntax error: <%.*s>: %s",
          (int)GRN_TEXT_LEN(&message), GRN_TEXT_VALUE(&message),
          errbuf);
    }
    GRN_OBJ_FIN(ctx, &message);
  }
}

input ::= query.
input ::= expression.
input ::= START_OUTPUT_COLUMNS output_columns.
input ::= START_ADJUSTER adjuster.
input ::= START_SORT_KEYS sort_keys.
input ::= START_OPTIONS object_literal.
/* Accept empty input with QUERY_NO_SYNTAX_ERROR. */
input ::= START_QUERY_NO_SYNTAX_ERROR.
input ::= START_QUERY_NO_SYNTAX_ERROR query.

query ::= query_element.
query ::= query query_element. {
  grn_expr_append_op(efsi->ctx, efsi->e, grn_int32_value_at(&efsi->op_stack, -1), 2);
}
query ::= query LOGICAL_AND query_element. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND, 2);
}
query ::= query LOGICAL_AND_NOT query_element. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_NOT, 2);
}
query ::= query LOGICAL_OR query_element. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR, 2);
}
query ::= query NEGATIVE query_element. {
  float weight;
  GRN_FLOAT32_POP(&efsi->weight_stack, weight);
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ADJUST, 2);
}

query_element ::= QSTRING.
query_element ::= PARENL query PARENR.

query_element ::= ADJUST query_element. {
  float weight;
  GRN_FLOAT32_POP(&efsi->weight_stack, weight);
}
query_element ::= RELATIVE_OP query_element. {
  int mode;
  GRN_INT32_POP(&efsi->mode_stack, mode);
}
query_element ::= IDENTIFIER RELATIVE_OP query_element. {
  int mode;
  grn_obj *c;
  GRN_PTR_POP(&efsi->column_stack, c);
  GRN_INT32_POP(&efsi->mode_stack, mode);
  switch (mode) {
  case GRN_OP_NEAR :
  case GRN_OP_NEAR_NO_OFFSET :
    {
      int max_interval;
      GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
      grn_obj *max_element_intervals;
      GRN_PTR_POP(&efsi->max_element_intervals_stack,
                  max_element_intervals);
      int min_interval;
      GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    }
    break;
  case GRN_OP_NEAR_PHRASE :
  case GRN_OP_ORDERED_NEAR_PHRASE :
  case GRN_OP_NEAR_PHRASE_PRODUCT :
  case GRN_OP_ORDERED_NEAR_PHRASE_PRODUCT :
    {
      int max_interval;
      GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
      int additional_last_interval;
      GRN_INT32_POP(&efsi->additional_last_interval_stack,
                    additional_last_interval);
      grn_obj *max_element_intervals;
      GRN_PTR_POP(&efsi->max_element_intervals_stack,
                  max_element_intervals);
      int min_interval;
      GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    }
    break;
  case GRN_OP_SIMILAR :
    {
      int similarity_threshold;
      GRN_INT32_POP(&efsi->similarity_threshold_stack, similarity_threshold);
    }
    break;
  case GRN_OP_QUORUM :
    {
      int quorum_threshold;
      GRN_INT32_POP(&efsi->quorum_threshold_stack, quorum_threshold);
    }
    break;
  default :
    break;
  }
}
query_element ::= BRACEL expression BRACER. {
  efsi->flags = efsi->default_flags;
}
query_element ::= EVAL primary_expression. {
  efsi->flags = efsi->default_flags;
}

expression ::= assignment_expression.
expression ::= expression COMMA assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
}

assignment_expression ::= conditional_expression.
assignment_expression ::= lefthand_side_expression ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression STAR_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression SLASH_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression MOD_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression PLUS_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression MINUS_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression SHIFTL_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression SHIFTR_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression SHIFTRR_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression AND_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression XOR_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_XOR_ASSIGN, 2);
}
assignment_expression ::= lefthand_side_expression OR_ASSIGN assignment_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR_ASSIGN, 2);
}

conditional_expression ::= logical_or_expression.
conditional_expression ::= logical_or_expression QUESTION(A) assignment_expression COLON(B) assignment_expression. {
  grn_expr *e = (grn_expr *)efsi->e;
  e->codes[A].nargs = B - A;
  e->codes[B].nargs = e->codes_curr - B - 1;
}

logical_or_expression ::= logical_and_expression.
logical_or_expression ::= logical_or_expression LOGICAL_OR logical_and_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR, 2);
}

logical_and_expression ::= bitwise_or_expression.
logical_and_expression ::= logical_and_expression LOGICAL_AND bitwise_or_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND, 2);
}
logical_and_expression ::= logical_and_expression LOGICAL_AND_NOT bitwise_or_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_NOT, 2);
}
logical_and_expression ::= logical_and_expression ADJUST bitwise_or_expression. {
  float weight;
  GRN_FLOAT32_POP(&efsi->weight_stack, weight);
  grn_expr_append_const_float32(efsi->ctx, efsi->e, weight, GRN_OP_AND, 2);
}
logical_and_expression ::= logical_and_expression NEGATIVE bitwise_or_expression. {
  float weight;
  GRN_FLOAT32_POP(&efsi->weight_stack, weight);
  grn_expr_append_const_float32(efsi->ctx, efsi->e, weight, GRN_OP_ADJUST, 2);
}

bitwise_or_expression ::= bitwise_xor_expression.
bitwise_or_expression ::= bitwise_or_expression BITWISE_OR bitwise_xor_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_OR, 2);
}

bitwise_xor_expression ::= bitwise_and_expression.
bitwise_xor_expression ::= bitwise_xor_expression BITWISE_XOR bitwise_and_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_XOR, 2);
}

bitwise_and_expression ::= equality_expression.
bitwise_and_expression ::= bitwise_and_expression BITWISE_AND equality_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_AND, 2);
}

equality_expression ::= relational_expression.
equality_expression ::= equality_expression EQUAL relational_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EQUAL, 2);
}
equality_expression ::= equality_expression NOT_EQUAL relational_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT_EQUAL, 2);
}

relational_expression ::= shift_expression.
relational_expression ::= relational_expression LESS shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS, 2);
}
relational_expression ::= relational_expression GREATER shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER, 2);
}
relational_expression ::= relational_expression LESS_EQUAL shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS_EQUAL, 2);
}
relational_expression ::= relational_expression GREATER_EQUAL shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER_EQUAL, 2);
}
relational_expression ::= relational_expression IN shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_IN, 2);
}
relational_expression ::= relational_expression MATCH shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MATCH, 2);
}
relational_expression ::= relational_expression NEAR shift_expression. {
  int n_args = 2;
  do {
    int max_interval;
    GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    grn_obj *max_element_intervals;
    GRN_PTR_POP(&efsi->max_element_intervals_stack,
                max_element_intervals);
    int min_interval;
    GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    if (!max_element_intervals) {
      break;
    }
    grn_expr_append_obj(efsi->ctx, efsi->e, max_element_intervals,
                        GRN_OP_PUSH, 1);
    grn_expr_take_obj(efsi->ctx, efsi->e, max_element_intervals);
    n_args++;
    grn_expr_append_const_int(efsi->ctx, efsi->e, min_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
  } while (false);
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR, n_args);
}
relational_expression ::= relational_expression NEAR_NO_OFFSET shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR_NO_OFFSET, 2);
}
relational_expression ::= relational_expression NEAR_PHRASE shift_expression. {
  int n_args = 2;
  do {
    int max_interval;
    GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    int additional_last_interval;
    GRN_INT32_POP(&efsi->additional_last_interval_stack,
                  additional_last_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, additional_last_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    grn_obj *max_element_intervals;
    GRN_PTR_POP(&efsi->max_element_intervals_stack,
                max_element_intervals);
    int min_interval;
    GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    if (!max_element_intervals) {
      break;
    }
    grn_expr_append_obj(efsi->ctx, efsi->e, max_element_intervals,
                        GRN_OP_PUSH, 1);
    grn_expr_take_obj(efsi->ctx, efsi->e, max_element_intervals);
    n_args++;
    grn_expr_append_const_int(efsi->ctx, efsi->e, min_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
  } while (false);
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR_PHRASE, n_args);
}
relational_expression ::= relational_expression ORDERED_NEAR_PHRASE shift_expression. {
  int n_args = 2;
  do {
    int max_interval;
    GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    int additional_last_interval;
    GRN_INT32_POP(&efsi->additional_last_interval_stack,
                  additional_last_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, additional_last_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    grn_obj *max_element_intervals;
    GRN_PTR_POP(&efsi->max_element_intervals_stack,
                max_element_intervals);
    int min_interval;
    GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    if (!max_element_intervals) {
      break;
    }
    grn_expr_append_obj(efsi->ctx, efsi->e, max_element_intervals,
                        GRN_OP_PUSH, 1);
    grn_expr_take_obj(efsi->ctx, efsi->e, max_element_intervals);
    n_args++;
    grn_expr_append_const_int(efsi->ctx, efsi->e, min_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
  } while (false);
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ORDERED_NEAR_PHRASE, n_args);
}
relational_expression ::= relational_expression NEAR_PHRASE_PRODUCT shift_expression. {
  int n_args = 2;
  do {
    int max_interval;
    GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    int additional_last_interval;
    GRN_INT32_POP(&efsi->additional_last_interval_stack,
                  additional_last_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, additional_last_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    grn_obj *max_element_intervals;
    GRN_PTR_POP(&efsi->max_element_intervals_stack,
                max_element_intervals);
    int min_interval;
    GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    if (!max_element_intervals) {
      break;
    }
    grn_expr_append_obj(efsi->ctx, efsi->e, max_element_intervals,
                        GRN_OP_PUSH, 1);
    grn_expr_take_obj(efsi->ctx, efsi->e, max_element_intervals);
    n_args++;
    grn_expr_append_const_int(efsi->ctx, efsi->e, min_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
  } while (false);
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR_PHRASE_PRODUCT, n_args);
}
relational_expression ::= relational_expression ORDERED_NEAR_PHRASE_PRODUCT shift_expression. {
  int n_args = 2;
  do {
    int max_interval;
    GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    int additional_last_interval;
    GRN_INT32_POP(&efsi->additional_last_interval_stack,
                  additional_last_interval);
    grn_expr_append_const_int(efsi->ctx, efsi->e, additional_last_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
    grn_obj *max_element_intervals;
    GRN_PTR_POP(&efsi->max_element_intervals_stack,
                max_element_intervals);
    int min_interval;
    GRN_INT32_POP(&efsi->min_interval_stack, min_interval);
    if (!max_element_intervals) {
      break;
    }
    grn_expr_append_obj(efsi->ctx, efsi->e, max_element_intervals,
                        GRN_OP_PUSH, 1);
    grn_expr_take_obj(efsi->ctx, efsi->e, max_element_intervals);
    n_args++;
    grn_expr_append_const_int(efsi->ctx, efsi->e, min_interval,
                              GRN_OP_PUSH, 1);
    n_args++;
  } while (false);
  grn_expr_append_op(efsi->ctx,
                     efsi->e,
                     GRN_OP_ORDERED_NEAR_PHRASE_PRODUCT,
                     n_args);
}
relational_expression ::= relational_expression SIMILAR shift_expression. {
  {
    int similarity_threshold;
    GRN_INT32_POP(&efsi->similarity_threshold_stack, similarity_threshold);
    grn_expr_append_const_int(efsi->ctx, efsi->e, similarity_threshold,
                              GRN_OP_PUSH, 1);
  }
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SIMILAR, 3);
}
relational_expression ::= relational_expression TERM_EXTRACT shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_TERM_EXTRACT, 2);
}
relational_expression ::= relational_expression QUORUM shift_expression. {
  {
    int quorum_threshold;
    GRN_INT32_POP(&efsi->quorum_threshold_stack, quorum_threshold);
    grn_expr_append_const_int(efsi->ctx, efsi->e, quorum_threshold,
                              GRN_OP_PUSH, 1);
  }
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_QUORUM, 3);
}
relational_expression ::= relational_expression LCP shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LCP, 2);
}
relational_expression ::= relational_expression PREFIX shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PREFIX, 2);
}
relational_expression ::= relational_expression SUFFIX shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SUFFIX, 2);
}
relational_expression ::= relational_expression REGEXP shift_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_REGEXP, 2);
}

shift_expression ::= additive_expression.
shift_expression ::= shift_expression SHIFTL additive_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL, 2);
}
shift_expression ::= shift_expression SHIFTR additive_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR, 2);
}
shift_expression ::= shift_expression SHIFTRR additive_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR, 2);
}

additive_expression ::= multiplicative_expression.
additive_expression ::= additive_expression PLUS multiplicative_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 2);
}
additive_expression ::= additive_expression MINUS multiplicative_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 2);
}

multiplicative_expression ::= unary_expression.
multiplicative_expression ::= multiplicative_expression STAR unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR, 2);
}
multiplicative_expression ::= multiplicative_expression SLASH unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH, 2);
}
multiplicative_expression ::= multiplicative_expression MOD unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD, 2);
}

unary_expression ::= postfix_expression.
unary_expression ::= DELETE unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DELETE, 1);
}
unary_expression ::= INCR unary_expression. {
  grn_ctx *ctx = efsi->ctx;
  grn_expr *e = (grn_expr *)(efsi->e);
  grn_expr_dfi *dfi_;
  unsigned int const_p;

  dfi_ = grn_expr_dfi_pop(e);
  const_p = CONSTP(dfi_->code->value);
  grn_expr_dfi_put(ctx, e, dfi_->type, dfi_->domain, dfi_->code);
  if (const_p) {
    ERR(GRN_SYNTAX_ERROR,
        "constant can't be incremented: <%.*s>",
        (int)(efsi->str_end - efsi->str), efsi->str);
  } else {
    grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR, 1);
  }
}
unary_expression ::= DECR unary_expression. {
  grn_ctx *ctx = efsi->ctx;
  grn_expr *e = (grn_expr *)(efsi->e);
  grn_expr_dfi *dfi_;
  unsigned int const_p;

  dfi_ = grn_expr_dfi_pop(e);
  const_p = CONSTP(dfi_->code->value);
  grn_expr_dfi_put(ctx, e, dfi_->type, dfi_->domain, dfi_->code);
  if (const_p) {
    ERR(GRN_SYNTAX_ERROR,
        "constant can't be decremented: <%.*s>",
        (int)(efsi->str_end - efsi->str), efsi->str);
  } else {
    grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR, 1);
  }
}
unary_expression ::= PLUS unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 1);
}
unary_expression ::= MINUS unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 1);
}
unary_expression ::= NOT unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT, 1);
}
unary_expression ::= BITWISE_NOT unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_NOT, 1);
}
unary_expression ::= ADJUST unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ADJUST, 1);
}
unary_expression ::= EXACT unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EXACT, 1);
}
unary_expression ::= PARTIAL unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PARTIAL, 1);
}
unary_expression ::= UNSPLIT unary_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_UNSPLIT, 1);
}

postfix_expression ::= lefthand_side_expression.
postfix_expression ::= lefthand_side_expression INCR. {
  grn_ctx *ctx = efsi->ctx;
  grn_expr *e = (grn_expr *)(efsi->e);
  grn_expr_dfi *dfi_;
  unsigned int const_p;

  dfi_ = grn_expr_dfi_pop(e);
  const_p = CONSTP(dfi_->code->value);
  grn_expr_dfi_put(ctx, e, dfi_->type, dfi_->domain, dfi_->code);
  if (const_p) {
    ERR(GRN_SYNTAX_ERROR,
        "constant can't be incremented: <%.*s>",
        (int)(efsi->str_end - efsi->str), efsi->str);
  } else {
    grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR_POST, 1);
  }
}
postfix_expression ::= lefthand_side_expression DECR. {
  grn_ctx *ctx = efsi->ctx;
  grn_expr *e = (grn_expr *)(efsi->e);
  grn_expr_dfi *dfi_;
  unsigned int const_p;

  dfi_ = grn_expr_dfi_pop(e);
  const_p = CONSTP(dfi_->code->value);
  grn_expr_dfi_put(ctx, e, dfi_->type, dfi_->domain, dfi_->code);
  if (const_p) {
    ERR(GRN_SYNTAX_ERROR,
        "constant can't be decremented: <%.*s>",
        (int)(efsi->str_end - efsi->str), efsi->str);
  } else {
    grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR_POST, 1);
  }
}

lefthand_side_expression ::= call_expression.
lefthand_side_expression ::= member_expression.

call_expression ::= member_expression arguments(A). {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_CALL, A);
}

member_expression ::= primary_expression.
member_expression ::= member_expression member_expression_part.

primary_expression ::= object_literal.
primary_expression ::= PARENL expression PARENR.
primary_expression ::= IDENTIFIER.
primary_expression ::= array_literal.
primary_expression ::= DECIMAL.
primary_expression ::= HEX_INTEGER.
primary_expression ::= STRING.
primary_expression ::= BOOLEAN.
primary_expression ::= NULL.

array_literal ::= BRACKETL element_list(N) BRACKETR. {
  array_literal_end(efsi, N);
}

element_list(N) ::= . {
  N = 0;
}
element_list(N) ::= assignment_expression. {
  N = 1;
}
element_list(N) ::= element_list(N_SUB) COMMA assignment_expression. {
  N = N_SUB + 1;
}

object_literal ::= BRACEL property_list(N) BRACER. {
  object_literal_end(efsi, N);
}

property_list(N) ::= . {
  N = 0;
}
property_list(N) ::= property. {
  N = 1;
}
property_list(N) ::= property_list(N_SUB) COMMA property. {
  N = N_SUB + 1;
}
property ::= property_name COLON assignment_expression.

property_name ::= STRING.

member_expression_part ::= BRACKETL expression BRACKETR. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GET_MEMBER, 2);
}
member_expression_part ::= DOT IDENTIFIER.

arguments(A) ::= PARENL argument_list(B) PARENR. { A = B; }
argument_list(A) ::= . { A = 0; }
argument_list(A) ::= assignment_expression. { A = 1; }
argument_list(A) ::= argument_list(B) COMMA assignment_expression. { A = B + 1; }

output_columns(N_STACKED_COLUMNS) ::= . {
  N_STACKED_COLUMNS = 0;
}
output_columns(N_STACKED_COLUMNS) ::= output_column(SUB_N_STACKED_COLUMNS). {
  N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
}
/* Accept "column1,,,,,,column2" */
output_columns(N_STACKED_COLUMNS) ::=
   output_columns(SUB_N_STACKED_COLUMNS) COMMA. {
  N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
}
output_columns(N_STACKED_COLUMNS) ::=
   output_columns(SUB_N_STACKED_COLUMNS) COMMA
   output_column(NEW_N_STACKED_COLUMNS). {
  if (SUB_N_STACKED_COLUMNS == 0) {
    N_STACKED_COLUMNS = NEW_N_STACKED_COLUMNS;
  } else if (NEW_N_STACKED_COLUMNS == 0) {
    N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
  } else {
    if (NEW_N_STACKED_COLUMNS == 1) {
      grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
    }
    N_STACKED_COLUMNS = 1;
  }
}

output_column(N_STACKED_COLUMNS) ::= STAR. {
  grn_ctx *ctx = efsi->ctx;
  grn_obj *expr = efsi->e;
  grn_obj *variable = grn_expr_get_var_by_offset(ctx, expr, 0);
  if (variable) {
    grn_id table_id = GRN_OBJ_GET_DOMAIN(variable);
    grn_obj *table = grn_ctx_at(ctx, table_id);
    grn_obj columns_buffer;
    int n_columns;
    grn_obj **columns;

    GRN_PTR_INIT(&columns_buffer, GRN_OBJ_VECTOR, GRN_ID_NIL);
    grn_obj_columns(ctx, table, "*", strlen("*"), &columns_buffer);
    n_columns = GRN_BULK_VSIZE(&columns_buffer) / sizeof(grn_obj *);
    columns = (grn_obj **)GRN_BULK_HEAD(&columns_buffer);

    if (n_columns == 0) {
      /* do nothing */
    } else if (n_columns == 1) {
      grn_obj *column = columns[0];
      grn_expr_append_const(ctx, expr, column, GRN_OP_GET_VALUE, 1);
      grn_expr_take_obj(ctx, expr, column);
    } else {
      grn_expr *e = (grn_expr *)expr;
      bool have_column;
      int i;

      have_column = (e->codes_curr > 0);
      for (i = 0; i < n_columns; i++) {
        grn_obj *column = columns[i];
        grn_expr_append_const(ctx, expr, column, GRN_OP_GET_VALUE, 1);
        if (have_column || i > 0) {
          grn_expr_append_op(ctx, expr, GRN_OP_COMMA, 2);
        }
        grn_expr_take_obj(ctx, expr, column);
      }
    }

    grn_obj_unref(ctx, table);
    GRN_OBJ_FIN(ctx, &columns_buffer);

    N_STACKED_COLUMNS = n_columns;
  } else {
    /* TODO: report error */
    N_STACKED_COLUMNS = 0;
  }
}
output_column(N_STACKED_COLUMNS) ::= NONEXISTENT_COLUMN. {
  N_STACKED_COLUMNS = 0;
}
output_column(N_STACKED_COLUMNS) ::= assignment_expression. {
  N_STACKED_COLUMNS = 1;
}

adjuster ::= .
adjuster ::= adjust_expression.
adjuster ::= adjuster PLUS adjust_expression. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 2);
}

adjust_expression ::= adjust_match_expression.
adjust_expression ::= adjust_match_expression STAR DECIMAL. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR, 2);
}

adjust_match_expression ::= IDENTIFIER MATCH STRING. {
  grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MATCH, 2);
}

sort_keys(N_STACKED_COLUMNS) ::= . {
  N_STACKED_COLUMNS = 0;
}
sort_keys(N_STACKED_COLUMNS) ::= sort_key(SUB_N_STACKED_COLUMNS). {
  N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
}
sort_keys(N_STACKED_COLUMNS) ::=
   sort_keys(SUB_N_STACKED_COLUMNS) COMMA
   sort_key(NEW_N_STACKED_COLUMNS). {
  if (SUB_N_STACKED_COLUMNS == 0) {
    N_STACKED_COLUMNS = NEW_N_STACKED_COLUMNS;
  } else if (NEW_N_STACKED_COLUMNS == 0) {
    N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
  } else {
    if (NEW_N_STACKED_COLUMNS == 1) {
      grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
    }
    N_STACKED_COLUMNS = 1;
  }
}

sort_key(N_STACKED_COLUMNS) ::= NONEXISTENT_COLUMN. {
  N_STACKED_COLUMNS = 0;
}
sort_key(N_STACKED_COLUMNS) ::= MINUS NONEXISTENT_COLUMN. {
  N_STACKED_COLUMNS = 0;
}
sort_key(N_STACKED_COLUMNS) ::= assignment_expression. {
  N_STACKED_COLUMNS = 1;
}
